/*
 * MathUtils.cpp
 *
 *  Created on: 21-11-2010
 *      Author: Przemek
 */

#include "utils/MathUtils.hpp"
#include "utils/RangedValue.hpp"
#include "pch.hpp"
using namespace std;

const float MathUtils::PI = 3.14159265358979323846f;
const int MathUtils::FULL_CIRCLE_DEGREESES = 360;
const float MathUtils::EPSILON = 0.000001f;

vector<Vector> createUnitCircleCoordinates()
{
	vector<Vector> unitCircleCoordinates;

	for(int i = 0; i < MathUtils::FULL_CIRCLE_DEGREESES; ++i)
	{
		const float angleInRadians = MathUtils::toRadians(i);
		unitCircleCoordinates.push_back(Vector(cosf(angleInRadians), sinf(angleInRadians)));
	}

	return unitCircleCoordinates;
}

const vector<Vector> MathUtils::unitCircleCoordinates = createUnitCircleCoordinates();

bool MathUtils::isNearZero(const float value)
{
	return (fabsf(value) < EPSILON);
}

bool MathUtils::isEqual(const float first, const float second)
{
	return isNearZero(first - second);
}

float MathUtils::toRadians(float degreeses)
{
	static const float factor = PI / 180.0f;

	return degreeses * factor;
}

float MathUtils::toDegreeses(float radians)
{
	static const float factor = 180.0f / PI;

	return radians * factor;
}

float MathUtils::trimToPositiveRadians(float radians)
{
	if(radians < 0.0f)
	{
		while(radians < 0.0f)
		{
			radians += 2 * PI;
		}
	}
	else
	{
		while(radians > 2 * PI)
		{
			radians -= 2 * PI;
		}
	}
	return radians;
}

float MathUtils::trimToPositiveDegrees(float degreeses)
{
	static const float DEGREESES = static_cast<float> (FULL_CIRCLE_DEGREESES);
	if(degreeses < 0.0f)
	{
		while(degreeses < 0.0f)
		{
			degreeses += DEGREESES;
		}
	}
	else
	{
		while(degreeses > DEGREESES)
		{
			degreeses -= DEGREESES;
		}
	}
	return degreeses;
}

float MathUtils::trimValue(float value, float min, float max)
{
	if(value <= min)
	{
		return min;
	}
	else if(value >= max)
	{
		return max;
	}
	return value;
}

bool MathUtils::areEqual(int count, ...)
{
	va_list numbers;
	va_start(numbers, count);
	double value = va_arg(numbers, double);
	for(int i = 1; i < count; ++i)
	{
		double otherValue = va_arg(numbers, double);
		if(!MathUtils::isEqual(value, otherValue))
		{
			return false;
		}
	}
	va_end(numbers);
	return true;
}

float MathUtils::max(int count, ...)
{
	va_list numbers;
	va_start(numbers, count);
	double maximum = va_arg(numbers, double);
	for(int i = 1; i < count; ++i)
	{
		double value = va_arg(numbers, double);
		maximum = maximum > value? maximum : value;
	}
	va_end(numbers);
	return maximum;
}

float MathUtils::min(int count, ...)
{
	va_list numbers;
	va_start(numbers, count);
	double minimum = va_arg(numbers, double);
	for(int i = 1; i < count; ++i)
	{
		double value = va_arg(numbers, double);
		minimum = minimum < value? minimum : value;
	}
	va_end(numbers);
	return minimum;
}

Vector MathUtils::getUnitCircleCoordinatesForAngle(int angleInDegreeses)
{
	if(angleInDegreeses < 0)
	{
		angleInDegreeses += FULL_CIRCLE_DEGREESES;
	}
	return unitCircleCoordinates.at(angleInDegreeses % FULL_CIRCLE_DEGREESES);
}
